#ifndef BAIDU_TTS_H
#define BAIDU_TTS_H

#include <Arduino.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <driver/i2s.h>

class BaiduTTS {
private:
    String apiKey;      // API Key
    String secretKey;   // Secret Key
    String accessToken; // 访问令牌
    unsigned long tokenExpireTime; // 令牌过期时间（毫秒）
    
    // I2S配置
    i2s_port_t i2s_port = I2S_NUM_0;
    
    // 获取访问令牌
    bool getAccessToken() {
        if (apiKey.length() == 0 || secretKey.length() == 0) {
            Serial.println("API Key或Secret Key不能为空");
            return false;
        }
        
        HTTPClient http;
        String tokenUrl = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=" + apiKey + "&client_secret=" + secretKey;
        
        http.begin(tokenUrl);
        int httpCode = http.GET();
        
        if (httpCode == HTTP_CODE_OK) {
            String payload = http.getString();
            DynamicJsonDocument doc(1024);
            DeserializationError error = deserializeJson(doc, payload);
            
            if (!error) {
                accessToken = doc["access_token"].as<String>();
                tokenExpireTime = millis() + 29 * 24 * 60 * 60 * 1000;
                Serial.println("获取访问令牌成功: " + accessToken);
                http.end();
                return true;
            } else {
                Serial.println("解析访问令牌JSON失败");
            }
        } else {
            Serial.printf("获取访问令牌失败，HTTP错误: %d\n", httpCode);
        }
        
        http.end();
        return false;
    }
    
    // 检查令牌是否有效
    bool ensureValidToken() {
        if (accessToken.length() == 0 || millis() > tokenExpireTime) {
            return getAccessToken();
        }
        return true;
    }
    
    // 初始化I2S
    bool initI2S() {
        // 先尝试卸载可能存在的I2S驱动
        i2s_driver_uninstall(i2s_port);
        
        i2s_config_t i2s_config = {
            .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
            .sample_rate = 16000,
            .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
            .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
            .communication_format = I2S_COMM_FORMAT_STAND_I2S,
            .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
            .dma_buf_count = 32,  // 大幅增加DMA缓冲区数量，提高缓冲能力
            .dma_buf_len = 256,   // 大幅增加DMA缓冲区长度，减少中断频率
            .use_apll = true,     // 使用APLL提供更精确的时钟
            .tx_desc_auto_clear = true,
            .fixed_mclk = 0
        };
        
        i2s_pin_config_t pin_config = {
            .bck_io_num = 2,    // BCLK
            .ws_io_num = 3,     // LRC
            .data_out_num = 11,  // DIN
            .data_in_num = -1    // 不使用输入
        };
        
        esp_err_t err = i2s_driver_install(i2s_port, &i2s_config, 0, NULL);
        if (err != ESP_OK) {
            Serial.printf("I2S驱动安装失败，错误码：%d\n", err);
            return false;
        }
        
        err = i2s_set_pin(i2s_port, &pin_config);
        if (err != ESP_OK) {
            Serial.printf("I2S引脚配置失败，错误码：%d\n", err);
            i2s_driver_uninstall(i2s_port); // 清理已安装的驱动
            return false;
        }
        
        return true;
    }

public:
    BaiduTTS(String apiKey, String secretKey) : apiKey(apiKey), secretKey(secretKey), tokenExpireTime(0) {}
    
    // 初始化
    bool init() {
        return getAccessToken() && initI2S();
    }
    
    // 文字转语音
    bool textToSpeech(const String& text) {
        if (!ensureValidToken()) {
            return false;
        }
        // 清理I2S缓冲区，确保播放流畅
        clearBuffer();
        
        HTTPClient http;
        String apiUrl = "https://tsn.baidu.com/text2audio";
        String postData = "tex=" + text + 
                         "&tok=" + accessToken + 
                         "&cuid=ESP32Device" +
                         "&ctp=1" +
                         "&lan=zh" +
                         "&spd=5" +
                         "&pit=5" +
                         "&vol=9" +
                         "&per=1" +
                         "&aue=4"; // mp3格式
        
        http.begin(apiUrl);
        http.addHeader("Content-Type", "application/x-www-form-urlencoded");
        int httpCode = http.POST(postData);
        if (httpCode == HTTP_CODE_OK) {
            // 获取音频数据
            int len = http.getSize();
            // 增加缓冲区大小到8KB，提高数据处理效率
            const size_t bufferSize = 8192;
            uint8_t* buffer = (uint8_t*)malloc(bufferSize);
            if (!buffer) {
                Serial.println("内存分配失败");
                http.end();
                return false;
            }
            
            WiFiClient* stream = http.getStreamPtr();
            size_t written = 0;
            unsigned long lastWriteTime = 0;
            
            // 预缓冲更大量的数据再开始播放
            const size_t preBufferSize = 32768; // 预缓冲32KB数据
            uint8_t* preBuffer = (uint8_t*)malloc(preBufferSize);
            size_t preBufferFilled = 0;
            bool preBuffering = true;
            
            // 记录开始时间，用于计算缓冲速率
            unsigned long bufferStartTime = millis();
            
            if (!preBuffer) {
                Serial.println("预缓冲区内存分配失败，使用直接播放模式");
                preBuffering = false;
            }
            
            while(http.connected() && (len > 0 || len == -1)) {
                size_t size = stream->available();
                if(size) {
                    // 读取数据，最多读取缓冲区大小
                    int c = stream->readBytes(buffer, ((size > bufferSize) ? bufferSize : size));
                    
                    if (preBuffering && preBuffer) {
                        // 填充预缓冲区
                        if (preBufferFilled + c <= preBufferSize) {
                            memcpy(preBuffer + preBufferFilled, buffer, c);
                            preBufferFilled += c;
                            
                            // 检查是否已完成预缓冲
                            if (preBufferFilled >= preBufferSize || (len > 0 && preBufferFilled >= len)) {
                                Serial.printf("预缓冲完成，已缓冲 %d 字节\n", preBufferFilled);
                                preBuffering = false;
                                
                                // 一次性写入预缓冲的数据
                                size_t bytes_written = 0;
                                i2s_write(i2s_port, preBuffer, preBufferFilled, &bytes_written, portMAX_DELAY);
                                written += bytes_written;
                                
                                // 释放预缓冲区
                                free(preBuffer);
                                preBuffer = NULL;
                            }
                        } else {
                            // 预缓冲区已满，写入部分数据
                            size_t remainingSpace = preBufferSize - preBufferFilled;
                            memcpy(preBuffer + preBufferFilled, buffer, remainingSpace);
                            preBufferFilled += remainingSpace;
                            
                            Serial.printf("预缓冲完成，已缓冲 %d 字节\n", preBufferFilled);
                            preBuffering = false;
                            
                            // 一次性写入预缓冲的数据
                            size_t bytes_written = 0;
                            i2s_write(i2s_port, preBuffer, preBufferFilled, &bytes_written, portMAX_DELAY);
                            written += bytes_written;
                            
                            // 释放预缓冲区
                            free(preBuffer);
                            preBuffer = NULL;
                            
                            // 写入剩余数据
                            bytes_written = 0;
                            i2s_write(i2s_port, buffer + remainingSpace, c - remainingSpace, &bytes_written, portMAX_DELAY);
                            written += bytes_written;
                        }
                    } else {
                        // 直接写入I2S，使用较大的块大小
                        size_t bytes_written = 0;
                        i2s_write(i2s_port, buffer, c, &bytes_written, portMAX_DELAY);
                        written += bytes_written;
                        
                        // 自适应控制写入速率，避免数据堆积或缓冲区饥饿
                        unsigned long currentTime = millis();
                        // 计算写入速率（字节/秒）
                        float writeRate = (float)written / (currentTime - bufferStartTime + 1) * 1000;
                        
                        // 根据写入速率动态调整延迟
                        if (writeRate > 16000) { // 如果写入速率超过16KB/s
                            // 如果写入过快，给I2S更多时间处理
                            delay(10);
                        } else if (writeRate < 8000 && currentTime - lastWriteTime > 20) {
                            // 如果写入速率过慢且距离上次写入已有一定时间，不延迟
                        } else if (currentTime - lastWriteTime < 5) {
                            // 正常情况下的小延迟
                            delay(2);
                        }
                        lastWriteTime = millis();
                    }
                    
                    if(len > 0) {
                        len -= c;
                    }
                } else {
                    // 没有可用数据时，短暂等待
                    delay(5);
                }
            }
            
            // 确保预缓冲区被释放
            if (preBuffer) {
                // 如果还有预缓冲的数据，写入它们
                if (preBufferFilled > 0) {
                    size_t bytes_written = 0;
                    i2s_write(i2s_port, preBuffer, preBufferFilled, &bytes_written, portMAX_DELAY);
                    written += bytes_written;
                }
                free(preBuffer);
            }
            
            free(buffer);
            Serial.printf("播放完成，共写入 %d 字节\n", written);
            http.end();
            return true;
        } else {
            Serial.printf("HTTP错误: %d\n", httpCode);
            http.end();
            return false;
        }
    }
    
    // 停止播放
    void stop() {
        i2s_zero_dma_buffer(i2s_port);
    }
    
    // 清理I2S缓冲区
    void clearBuffer() {
        // 多次调用以确保缓冲区完全清空
        for (int i = 0; i < 3; i++) {
            i2s_zero_dma_buffer(i2s_port);
            // 短暂延迟，确保缓冲区完全清空
            delay(20);
        }
        
        // 重置I2S状态
        esp_err_t err = i2s_stop(i2s_port);
        if (err == ESP_OK) {
            err = i2s_start(i2s_port);
            if (err != ESP_OK) {
                Serial.printf("重启I2S失败，错误码：%d\n", err);
            }
        }
    }
    
    // 析构函数
    ~BaiduTTS() {
        i2s_driver_uninstall(i2s_port);
    }
};

#endif // BAIDU_TTS_H